Building Disposable Objects

As you have seen, finalizers can be used to release unmanaged resources when the garbage collector kicks in. However, given that many unmanaged objects are “precious items” (such as raw database or file handles), it may be valuable to release them as soon as possible instead of relying on a garbage collection to occur. As an alternative to overriding Finalize(), your class could implement the IDisposable interface, which defines a single method named Dispose():

public interface IDisposable
{
    void Dispose();
}

If you are new to interface-based programming, Chapter 9 will take you through the details. In a nutshell, an interface is a collection of abstract members a class or structure may support. When you do support the IDisposable interface, the assumption is that when the object user is finished using the object, the object user manually calls Dispose() before allowing the object reference to drop out of scope. In this way, an object can perform any necessary cleanup of unmanaged resources without incurring the hit of being placed on the finalization queue and without waiting for the garbage collector to trigger the class’s finalization logic.

Note Structures and class types can both implement IDisposable (unlike overriding Finalize(), which is reserved for class types), as the object user (not the garbage collector) invokes the Dispose() method

To illustrate the use of this interface, create a new C# Console Application named SimpleDispose. Here is an updated MyResourceWrapper class that now implements IDisposable, rather than overriding System.Object.Finalize():

// Implementing IDisposable.
class MyResourceWrapper : IDisposable
{
    // The object user should call this method
    // when they finish with the object.
    public void Dispose()
    {
        // Clean up unmanaged resources...

        // Dispose other contained disposable objects...

        // Just for a test.
        Console.WriteLine("***** In Dispose! *****");
    }
}

Notice that a Dispose() method is not only responsible for releasing the type’s unmanaged resources, but should also call Dispose() on any other contained disposable methods. Unlike with Finalize(), it is perfectly safe to communicate with other managed objects within a Dispose() method. The reason is simple: the garbage collector has no clue about the IDisposable interface and will never call Dispose(). Therefore, when the object user calls this method, the object is still living a productive life on the managed heap and has access to all other heap-allocated objects. The calling logic is straightforward:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("***** Fun with Dispose *****\n");

        // Create a disposable object and call Dispose()
        // to free any internal resources.

        MyResourceWrapper rw = new MyResourceWrapper();
        rw.Dispose();
        Console.ReadLine();
    }
}

Of course, before you attempt to call Dispose() on an object, you will want to ensure the type supports the IDisposable interface. While you will typically know which base class library types implement IDisposable by consulting the .NET Framework 4.0 SDK documentation, a programmatic check can be accomplished using the is or as keywords discussed in Chapter 6.:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("***** Fun with Dispose *****\n");
        MyResourceWrapper rw = new MyResourceWrapper();
        if (rw is IDisposable)
            rw.Dispose();
        Console.ReadLine();
    }
}

This example exposes yet another rule of working with garbage-collected types:

Rule Always call Dispose() on any object you directly create if the object supports IDisposable. The assumption you should make is that if the class designer chose to support the Dispose() method, the type has some cleanup to perform.

There is one caveat to the previous rule. A number of types in the base class libraries that do implement the IDisposable interface provide a (somewhat confusing) alias to the Dispose() method, in an attempt to make the disposal-centric method sound more natural for the defining type. By way of an example, while the System.IO.FileStream class implements IDisposable (and therefore supports a Dispose() method), it also defines a Close() method that is used for the same purpose:

// Assume you have imported
// the System.IO namespace...
static void DisposeFileStream()
{
    FileStream fs = new FileStream("myFile.txt", FileMode.OpenOrCreate);

    // Confusing, to say the least!
    // These method calls do the same thing!
    fs.Close();
    fs.Dispose();
}

While it does feel more natural to "close" a file rather than “dispose” of one, this doubling up of disposal-centric methods can be confusing. For the few types that do provide an alias, just remember that if a type implements IDisposable, calling Dispose() is always a correct course of action.

Reusing the C# using Keyword

When you are handling a managed object that implements IDisposable, it is quite common to make use of structured exception-handling to ensure the type’s Dispose() method is called in the event of a runtime exception:

static void Main(string[] args)
{
    Console.WriteLine("***    ** Fun with Dispose *****\n");
    MyResourceWrapper rw = new MyResourceWrapper ();
    try
    {
        // Use the members of rw.
    }
    finally
    {
        // Always call Dispose(), error or not.
        rw.Dispose();
    }
}

While this is a fine example of defensive programming, the truth of the matter is that few developers are thrilled by the prospects of wrapping each and every disposable type within a try/finally block just to ensure the Dispose() method is called. To achieve the same result in a much less obtrusive manner, C# supports a special bit of syntax that looks like this:

static void Main(string[] args)
{
    Console.WriteLine("***** Fun with Dispose *****\n");
    // Dispose() is called automatically when the
    // using scope exits.
    using(MyResourceWrapper rw = new MyResourceWrapper())
    {
        // Use rw object.
    }
}

If you looked at the CIL code of the Main() method using ildasm.exe, you would find the using syntax does indeed expand to try/final logic, with the expected call to Dispose():

.method private hidebysig static void Main(string[] args) cil managed
{
...
	.try
	{
		...
	} // end .try
	finally
	{
...
	IL_0012: callvirt instance void
		SimpleFinalize.MyResourceWrapper::Dispose()
} // end handler
...
} // end of method Program::Main
        

Note If you attempt to “use” an object that does not implement IDisposable, you will receive a compiler error.

While this syntax does remove the need to manually wrap disposable objects within try/finally logic, the C# using keyword unfortunately now has a double meaning (importing namespaces and invoking a Dispose() method). Nevertheless, when you are working with .NET types that support the IDisposable interface, this syntactical construct will ensure that the object “being used” will automatically have its Dispose() method called once the using block has exited.

Also, be aware that it is possible to declare multiple objects of the same type within a using scope. As you would expect, the compiler will inject code to call Dispose() on each declared object:

static void Main(string[] args)
{
    Console.WriteLine("***** Fun with Dispose *****\n");

    // Use a comma-delimited list to declare multiple objects to dispose.
    using(MyResourceWrapper rw = new MyResourceWrapper(),
        rw2 = new MyResourceWrapper())
    {
        // Use rw and rw2 objects.
    }
}

Source Code The SimpleDispose project is included under the Chapter 8 subdirectory.